package com.ht.web360.UserServer.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ht.web360.UserServer.dao.UserInfo;
import com.ht.web360.UserServer.mapper.UserInfoMapper;
import com.ht.web360.base.dto.RPage;
import com.ht.web360.base.dto.UserInfo.dto.UserInfoDto;
import com.ht.web360.base.dto.UserInfo.vo.UserLoginVo;
import com.ht.web360.base.enums.UserType;
import com.ht.web360.base.feignApi.Oss;
import com.ht.web360.base.utils.RedisIdWorker;
import com.ht.web360.base.utils.ThreadLocalUtil;
import com.ht.web360.base.utils.TokenUtil;
import com.ht.web360.base.utils.UserPwdUtil;
import com.ht.web360.base.utils.exceptions.MessageException;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

@Service
@Slf4j
public class UserInfoService {
    
    @Lazy
    @Autowired
    private UserInfoService _this;

    private final UserInfoMapper userInfoMapper;
    private final RedisIdWorker redisIdWorker;
    private final Oss oss;

    public UserInfoService(UserInfoMapper userInfoMapper, RedisIdWorker redisIdWorker, Oss oss) {
        this.userInfoMapper = userInfoMapper;
        this.redisIdWorker = redisIdWorker;
        this.oss = oss;
    }

    /**
     * 用户登录
     * @param userLogin 登录数据
     * @return token
     */
    public String login(UserLoginVo userLogin) {
        if (userLogin == null) {
            throw new MessageException("参数为空");
        }
        String account = userLogin.getAccount();
        if (StrUtil.isBlankIfStr(account)) {
            throw new MessageException("参数为空");
        }
        var query = new LambdaQueryChainWrapper<>(userInfoMapper);
        query.eq(UserInfo::getAccount, account)
                .last("limit 1");
        UserInfo userInfo = userInfoMapper.selectOne(query.getWrapper());
        if (userInfo != null) {
            if (UserPwdUtil.matches(userInfo.getPassword(), userLogin.getPassword())) {
                return TokenUtil.createToken(userInfo.toDto());
            } else {
                throw new MessageException("登录失败，密码错误");
            }
        } else {
            throw new MessageException("账号不存在");
        }
    }

    /**
     * 通过id获取用户信息
     * @return 用户信息
     */
    public UserInfoDto getById(String id) {
        if (StrUtil.isBlank(id)) {
            throw new MessageException("参数为空");
        }
        // 走缓存
        return _this.getById(Long.parseLong(id));
    }

    /**
     * 通过id获取用户信息
     * @return 用户信息
     */
    @Cacheable(cacheNames = "UserInfoDto", key = "#p0")
    public UserInfoDto getById(long id) {
        UserInfo userInfo = userInfoMapper.selectById(id);
        if (userInfo == null) {
            throw new MessageException("用户不存在");
        }
        return userInfo.toDto();
    }

    /**
     * 分页查询用户信息
     * @param current 当前页
     * @param size 单页大小
     * @return 分页数据
     */
    public RPage<UserInfoDto> pageUserInfo(long current, long size) {
        IPage<UserInfo> page = new Page<>(current, size);
        var query = new LambdaQueryChainWrapper<>(userInfoMapper);
        userInfoMapper.selectPage(page, query.getWrapper());
        return RPage.getInstance(page, UserInfo::toDto);
    }

    /**
     * 保存用户
     * @param userInfo 新用户数据
     * @return 新用户id
     */
    @Transactional
    @CacheEvict("UserInfoDto")
    public long save(UserInfo userInfo) {
        UserInfo u = new UserInfo();
        BeanUtil.copyProperties(userInfo, u);
        u.setId(redisIdWorker.nextId());
        u.setCreateTime(null);
        u.setUpdateTime(null);
        u.setPassword(UserPwdUtil.encode(userInfo.getPassword()));
        u.setUserType(UserType.User);
        try {
            userInfoMapper.insert(u);
        } catch (DataIntegrityViolationException e) {
            if (e.getMessage().contains("Duplicate entry")) {
                throw new MessageException("账号重复，请重新输入");
            }
        }
        return u.getId();
    }

    @Transactional
    public String updateAvatar(MultipartFile file) {
        Long userId = ThreadLocalUtil.getUserId();
        return _this.updateAvatar(userId, file);
    }

    /**
     * 不要调用这个方法
     */
    @Transactional
    @CacheEvict(cacheNames = "UserInfoDto", key = "#p0")
    public String updateAvatar(Long id, MultipartFile file) {
        try (
                InputStream is = file.getInputStream()
        ) {
            FastByteArrayOutputStream read = IoUtil.read(is);
            byte[] data = read.toByteArray();
            if (read.size() > 1024 * 1024) {
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                Thumbnails.of(new ByteArrayInputStream(data))
                        .size(200, 200)
                        .toOutputStream(os);
                data = os.toByteArray();
            }
            String linkMd5 = oss.upload(data);

            var query = new LambdaQueryChainWrapper<>(UserInfo.class);
            query.eq(UserInfo::getId, id);
            query.last("limit 1");

            UserInfo userInfo = userInfoMapper.selectOne(query.getWrapper());
            if (userInfo == null) {
                throw new MessageException("用户不存在");
            }
            String avatar = userInfo.getAvatar();
            try {
                if (StrUtil.isNotBlank(avatar) && !avatar.equals(linkMd5)) {
                    oss.delete(avatar);
                }
            } catch (MessageException e) {
                log.info(e.getMessage());
            }
            userInfo.setId(id);
            userInfo.setAvatar(linkMd5);
            userInfoMapper.updateById(userInfo);
            return TokenUtil.createToken(userInfo.toDto());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过id更新用户
     * @param userInfoDto 包含id的用户数据
     */
    @Transactional
    @CacheEvict(cacheNames = "UserInfoDto", key = "#p0.id")
    public String updateById(UserInfoDto userInfoDto) {
        if (userInfoDto.getId() == null || !NumberUtil.isLong(userInfoDto.getId())) {
            throw new MessageException("用户id错误");
        }
        UserInfoDto userDto = ThreadLocalUtil.getUserDto();
        if (userDto.getUserType() != UserType.Admin && !userInfoDto.getId().equals(userDto.getId())) {
            throw new MessageException("权限不足");
        }
        UserInfo userInfo = UserInfo.fromDto(userInfoDto);
        userInfo.setCreateTime(null);
        userInfo.setUpdateTime(null);
        userInfo.setAvatar(null);
        userInfo.setPassword(UserPwdUtil.encode(userInfo.getPassword()));
        try {
            userInfoMapper.updateById(userInfo);
        } catch (DataIntegrityViolationException e) {
            if (e.getMessage().contains("Duplicate entry")) {
                throw new MessageException("账号重复，请重新输入");
            }
        }
        userInfo = userInfoMapper.selectById(userInfo.getId());
        return TokenUtil.createToken(userInfo.toDto());
    }

    /**
     * 通过id删除用户
     */
    @Transactional
    @CacheEvict(cacheNames = "UserInfoDto", key = "#p0")
    public void deleteById(long id) {
        userInfoMapper.deleteById(id);
    }
}
